<?php
/**
 * Created by PhpStorm.
 * User: ari
 * Date: 10/13/14
 * Time: 3:43 PM
 */
namespace BC\Games\Holdem\Hands;

use BC\Games\Holdem\Cards;
use BC\Games\Holdem\Deck;
use CPath\UnitTest\ITestable;
use CPath\UnitTest\IUnitTestRequest;

class TwoPairHand implements IPokerHand, ITestable
{
	const RANK = 2000;

	private $mHighPair;
	private $mLowPair;
	private $mKicker;
	public function __construct(Array $highPair, Array $lowPair, $kicker) {
		$this->mHighPair = $highPair;
		$this->mLowPair = $lowPair;
		$this->mKicker = $kicker;
	}

	/**
	 * Return name of this hand
	 * @return String
	 */
	function getName() {
		return "Two Pair";
	}

	function getCards($withNullDelimiter=null) {
		$cards = array_merge(
			$this->mHighPair,
			array(null),
			$this->mLowPair,
			array(null),
			array($this->mKicker)
		);
		return $withNullDelimiter ? $cards : array_filter($cards);

	}

	/**
	 * Get Hand rank
	 * @return int
	 */
	function getRank() {
		return self::RANK
			+ ($this->mHighPair[0] & Cards::NUMBER_MASK)
			+ ($this->mLowPair[0] & Cards::NUMBER_MASK)/100
			+ ($this->mKicker[0] & Cards::NUMBER_MASK)/10000;
	}

	/**
	 * Compare two hands
	 * @param IPokerHand $Hand
	 * @return int 0, <0, or >0
	 */
	function compare(IPokerHand $Hand) {
		$rank = $this->getRank() - $Hand->getRank();
		if(!$Hand instanceof TwoPairHand)
			return $rank;

		$rank = $this->mHighPair[0] - $Hand->mHighPair[0];
		if($rank !== 0)
			return $rank;

		$rank = $this->mLowPair[0] - $Hand->mLowPair[0];
		if($rank !== 0)
			return $rank;

		$rank = $this->mKicker - $Hand->mKicker;

		return $rank;
	}

	/**
	 * Returns a string representation of this poker hand
	 * @return String
	 */
	function __toString() {
		return
			"[" . Cards::printCards($this->mHighPair[0])
			. ", " . Cards::printCards($this->mHighPair[1])
			. "], [" . Cards::printCards($this->mLowPair[0])
			. ", " . Cards::printCards($this->mLowPair[1])
			. "], " . Cards::printCards($this->mKicker);
	}

	/**
	 * Tests a poker hand to see if it matches an array of cards
	 * @param array $cards
	 * @param IPokerHand[] $Matches
	 * @return bool true if the set of cards matches this poker hand
	 */
	static function match(Array $cards, &$Matches = array()) {

		$matches = array();
		foreach ($cards as $card) {
			$num = $card & Cards::NUMBER_MASK;
			if (isset($matches[$num])) {
				$matches[$num][] = $card;

			} else {
				$matches[$num] = array($card);
			}
		}

		$pairs = array();
		foreach($matches as $num => $match) {
			if(sizeof($match) === 2) {
				$pairs[] = $match;
			}
		}

		if(sizeof($pairs) > 2) {
			usort($pairs, function($pair1, $pair2) {
				return $pair2[0] - $pair1[0];
			});
			$pairs = array_slice($pairs, 0, 2);
		}

		if(sizeof($pairs) === 2) {

			if($pairs[0][0] > $pairs[1][0])
				list($highPair, $lowPair) = $pairs;
			else
				list($lowPair, $highPair) = $pairs;

			$kickers = array_diff($cards, $highPair, $lowPair);
			rsort($kickers);
			$kicker = $kickers[0];

			$Matches[] = new TwoPairHand($highPair, $lowPair, $kicker);
			return true;
		}

		return false;
	}

	// Static

	/**
	 * Perform a unit test
	 * @param IUnitTestRequest $Test the unit test request inst for this test session
	 * @return void
	 * @test --disable 0
	 * Note: Use doctag 'test' with '--disable 1' to have this ITestable class skipped during a build
	 */
	static function handleStaticUnitTest(IUnitTestRequest $Test) {
		$Deck = new Deck();
		$Deck->replay(Cards::ACE | Cards::HEARTS, 'player1');
		$Deck->replay(Cards::FIVE | Cards::CLUBS, 'player1');
		$Deck->replay(Cards::THREE | Cards::DIAMONDS, 'board');
		$Deck->replay(Cards::SEVEN | Cards::HEARTS, 'board');
		$Deck->replay(Cards::THREE | Cards::SPADES, 'board');
		$Deck->replay(Cards::TWO | Cards::CLUBS, 'board');
		$Deck->replay(Cards::ACE | Cards::DIAMONDS, 'board');

		$cards = $Deck->getDrawnCards('player1', 'board');
		if(!TwoPairHand::match($cards, $Hands))
			$Test->fail("Two Pair not matched to cards: " . Cards::printCards($cards));

		$Test->log("Matched Two Pair to " . $Hands[0] . ' from ' . Cards::printCards($cards), $Test::VERBOSE);
	}
}

